﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// NotReviewedButNeeded
#pragma once

#include "NdbObjectContainer.h"
#include "NdbObjectArchiveHelper.h"
#include "NdbPropertySerialize.h"
#include "NdbReplaceElementIdHelper.h"
#include "NdbReportElementIdHelper.h"
#include "NdbCopyHelper.h"
#include "NdbObjectMemoryModelGroupNode.h"
#include "NdbArchiveBlockHeader.h"
#include <map>

namespace gcmp
{
    class IDbObjectArchive;
    template <class Key, class T, class KeyCompare = std::less<Key>>
    class NdbMap : public NdbObjectContainer
    {
        typedef std::true_type is_map;
        // type/iter
    public:
        typedef typename std::map<Key, T, KeyCompare>::key_type     key_type;
        typedef typename std::map<Key, T, KeyCompare>::value_type   value_type;
        typedef typename std::map<Key, T, KeyCompare>::size_type    size_type;
        typedef typename std::map<Key, T, KeyCompare>::key_compare  key_compare_type;

        typedef typename std::map<Key, T, KeyCompare>::iterator                 iterator;
        typedef typename std::map<Key, T, KeyCompare>::const_iterator           const_iterator;
        typedef typename std::map<Key, T, KeyCompare>::reverse_iterator         reverse_iterator;
        typedef typename std::map<Key, T, KeyCompare>::const_reverse_iterator   const_reverse_iterator;

        // constructor/destructor/operator=
    public:
        NdbMap() {}

        NdbMap(const NdbMap& another)
        {
            m_map = another.m_map;
        }
        NdbMap(const std::map<Key,T, KeyCompare>& map)
        {
            m_map = map;
        }

        NdbMap& operator = (const NdbMap& another)
        {
            if (this != &another)
            {
                m_map = another.m_map;
            }
            return *this;
        }

        NdbMap& operator = (NdbMap&& another)
        {
            if (this != &another)
            {
                m_map = std::move(another.m_map);
            }
            return *this;
        }

        NdbMap& operator = (const std::map<Key, T, KeyCompare>& map)
        {
            if (&m_map != &map)
            {
                m_map = map;
            }
            return *this;
        }

        NdbMap& operator = (std::map<Key, T, KeyCompare>&& map)
        {
            if (&m_map != &map)
            {
                m_map = std::move(map);
            }
            return *this;
        }

        const std::map<Key, T, KeyCompare>& StdMap() const
        {
            return m_map;
        }

        std::map<Key, T, KeyCompare>& StdMapFW()
        {
            return m_map;
        }

        operator const std::map<Key,T, KeyCompare>& () const
        {
            return m_map;
        }

        operator std::map<Key,T, KeyCompare>& ()
        {
            return m_map;
        }

        // Iterators
    public:
        iterator begin()
        {
            return m_map.begin();
        }

        const_iterator begin() const
        {
            return m_map.begin();
        }

        iterator end()
        {
            return m_map.end();
        }

        const_iterator end() const
        {
            return m_map.end();
        }

        reverse_iterator rbegin() 
        {
            return m_map.rbegin();
        }

        const_reverse_iterator rbegin() const
        {
            return m_map.rbegin();
        }

        reverse_iterator rend()
        {
            return m_map.rend();
        }

        const_reverse_iterator rend() const
        {
            return m_map.rend();
        }

        const_iterator cbegin() const
        {
            return m_map.begin();
        }

        const_iterator cend() const
        {
            return m_map.cend();
        }

        const_reverse_iterator crbegin() const
        {
            return m_map.rbegin();
        }

        const_reverse_iterator crend() const
        {
            return m_map.rend();
        }

        // Modifiers
    public:
        template <class P>
        std::pair<iterator, bool> insert(P&& val)
        {
            return m_map.insert(std::forward<P>(val));
        }

        size_type erase(const Key& k)
        {
            return m_map.erase(k);
        }

        void clear()
        {
            m_map.clear();
        }

        // Capacity
    public:
        bool empty() const
        {
            return m_map.empty();
        }

        size_type size() const
        {
            return m_map.size();
        }

        size_type max_size() const
        {
            return m_map.max_size();
        }

        // Element access
    public:
        T& operator[](const Key& k)
        {
            return m_map[k];
        }

        T& operator[](Key&& k)
        {
            return m_map[std::forward<Key>(k)];
        }

        T& at(const Key& k)
        {
            return m_map.at(k);
        }

        const T& at(const Key& k) const
        {
            return m_map.at(k);
        }

        // Operations
    public:
        iterator find(const key_type& k)
        {
            return m_map.find(k);
        }

        const_iterator find(const key_type& k) const
        {
            return m_map.find(k);
        }

        size_type count(const key_type& k) const
        {
            return m_map.count(k);
        }

        iterator lower_bound(const key_type& k)
        {
            return m_map.lower_bound(k);
        }

        const_iterator lower_bound(const key_type& k) const
        {
            return m_map.lower_bound(k);
        }

        iterator upper_bound(const key_type& k)
        {
            return m_map.upper_bound(k);
        }

        const_iterator upper_bound(const key_type& k) const
        {
            return m_map.upper_bound(k);
        }

        std::pair<iterator, iterator> equal_range(const key_type& k)
        {
            return m_map.equal_range(k);
        }

        std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const
        {
            return m_map.equal_range(k);
        }

        // 实现NdbObjectCloneable接口
    public:
        virtual NdbPropertySchemaType GetSchemaType() const override
        {
            return NdbPropertySchemaType::Map;
        }

        virtual gcmp::OwnerPtr<NdbObjectContainer> NdbClone() const override
        { 
            OwnerPtr<NdbMap> opClonedNdbMap = NEW_AS_OWNER_PTR(NdbMap);
            for (auto ite = m_map.begin(); ite != m_map.end(); ++ite)
            {
                Key k = NdbCopyHelper<Key>::Copy(ite->first);
                T t = NdbCopyHelper<T>::Copy(ite->second);
                (*opClonedNdbMap)[k] = std::move(t);
            }
            return TransferOwnership(opClonedNdbMap);
        }

        virtual void NdbAssignByMove(gcmp::OwnerPtr<NdbObjectContainer> opSrc) override
        {
            NdbMap * pMap = dynamic_cast<NdbMap*>(opSrc.get());
            if (pMap)
            {
                *this = std::move(*pMap);
            }
        }

        virtual UInt64 NdbSerialize(IDbObjectArchive & objectArchive, NdbPropertyUtils::BehavioursAdapter adapter) const override
        {
            UInt64 nSize = size();
            NdbContainerArchiveBlockHeader containerArchiveBlockHeader(NdbContainerArchiveBlockHeader::s_CurrentVersion, nSize, NdbPropertySchemaType::Map);
            
            if (property_is_value_type<Key>::value && property_is_value_type<T>::value)
            {
                NdbArchiveBlockHeader blockHeader = GetSharedBlockHeader<Key>();
                containerArchiveBlockHeader.SetSharedHeader(blockHeader, 0);

                blockHeader = GetSharedBlockHeader<T>();
                containerArchiveBlockHeader.SetSharedHeader(blockHeader, 1);

                UInt64 length = containerArchiveBlockHeader.Serialize(objectArchive.GetIOStream());

                for (auto ite = m_map.begin(); ite != m_map.end(); ++ite)
                {
                    length += NdbPropertySerializeWithoutHeader<Key>::Serialize(ite->first, objectArchive, adapter);
                    length += NdbPropertySerializeWithoutHeader<T>::Serialize(ite->second, objectArchive, adapter);
                }

                return length;
            }
            else
            {
                UInt64 length = containerArchiveBlockHeader.Serialize(objectArchive.GetIOStream());
                for (auto ite = m_map.begin(); ite != m_map.end(); ++ite)
                {
                    length += NdbPropertySerialize<Key>::Serialize(ite->first, objectArchive, adapter);
                    length += NdbPropertySerialize<T>::Serialize(ite->second, objectArchive, adapter);
                }
                return length;
            }
        }

        virtual void NdbExtractObjectFromMemory(NdbObjectMemoryModelNode * pObjMemModel, UInt32 majorVersion) override
        {
            NdbObjectMemoryModelGroupNode *pMemGroupNode = thread_safe_quick_cast<NdbObjectMemoryModelGroupNode>(pObjMemModel);
            DBG_WARN_AND_RETURN_VOID_UNLESS(pMemGroupNode, L"pMemGroupNode为空？", L"GMEP", L"2020/11/24");
            std::vector<OwnerPtr<NdbObjectMemoryModelNode>>& subNodes = pMemGroupNode->GetSubNodesFW();
            UInt32 nSize = (UInt32)subNodes.size();
            for (UInt32 i = 0; i < nSize; i = i + 2)
            {
                NdbObjectMemoryModelNode *pSubNode = subNodes[i].get();
                if (!pSubNode) continue;
                Key k;
                switch (pSubNode->GetSchemaType())
                {
                case NdbPropertySchemaType::Value:
                    NdbObjectArchiveHelper::GetValueFromMemoryModel(&k, pSubNode);
                    break;
                case NdbPropertySchemaType::ElementId:
                    NdbObjectArchiveHelper::GetElementIdFromMemoryModel(&k, pSubNode);
                    break;
                case NdbPropertySchemaType::WString:
                    NdbObjectArchiveHelper::GetWStringFromMemoryModel(&k, pSubNode);
                    break;
                case NdbPropertySchemaType::SchematicObject:
                    NdbObjectArchiveHelper::GetSchematicObjectFromMemoryModel(&k, pSubNode, false/*isParentSubObject*/, majorVersion);
                    break;
                case NdbPropertySchemaType::CloneableObject:
                    NdbObjectArchiveHelper::GetCloneableObjectFromMemoryModel(&k, pSubNode, majorVersion);
                    break;
                case NdbPropertySchemaType::NativePointer:
                    NdbObjectArchiveHelper::GetPointerFromMemoryModel(&k, pSubNode);
                    break;
                default:
                    DBG_WARN(L"NdbMap Key: 不支持存放的类型" + NdbPropertyUtils::SchemaTypeAsWString(pSubNode->GetSchemaType()), L"GMEP", L"2017-01-16");
                    break;
                }

                pSubNode = subNodes[i + 1].get();
                if (!pSubNode) continue;
                T t;
                switch (pSubNode->GetSchemaType())
                {
                case NdbPropertySchemaType::Value:
                    NdbObjectArchiveHelper::GetValueFromMemoryModel(&t, pSubNode);
                    break;
                case NdbPropertySchemaType::ElementId:
                    NdbObjectArchiveHelper::GetElementIdFromMemoryModel(&t, pSubNode);
                    break;
                case NdbPropertySchemaType::WString:
                    NdbObjectArchiveHelper::GetWStringFromMemoryModel(&t, pSubNode);
                    break;
                case NdbPropertySchemaType::SchematicObject:
                    NdbObjectArchiveHelper::GetSchematicObjectFromMemoryModel(&t, pSubNode, false/*isParentSubObject*/, majorVersion);
                    break;
                case NdbPropertySchemaType::CloneableObject:
                    NdbObjectArchiveHelper::GetCloneableObjectFromMemoryModel(&t, pSubNode, majorVersion);
                    break;
                case NdbPropertySchemaType::OwnerPtrToNdbObject:
                    NdbObjectArchiveHelper::GetOwnerPtrFromMemoryModel(&t, pSubNode, majorVersion);
                    break;
                case NdbPropertySchemaType::OwnerPtrToIDbObject:
                    NdbObjectArchiveHelper::GetOwnerPtrToIDbObjectFromMemoryModel(&t, pSubNode, majorVersion);
                    break;
                case NdbPropertySchemaType::NativePointer:
                    NdbObjectArchiveHelper::GetPointerFromMemoryModel(&t, pSubNode);
                    break;
                default:
                    DBG_WARN(L"NdbMap Value: 不支持存放的类型" + NdbPropertyUtils::SchemaTypeAsWString(pSubNode->GetSchemaType()), L"GMEP", L"2019-03-30");
                    break;
                }
                m_map.insert(m_map.end(), { k, std::move(t) });
            }
        }

        virtual bool HasElementIdProperty() const override
        {
            if (m_map.empty()) return false;

            // Key或Value的类型是OwnerPtr时，不能通过第一个元素确定。详见ModelIdDetectorHelper的OwnerPtr特化版本说明
            FOR_EACH(iterMap, m_map)
            {
                if (ElementIdDetectorHelper<Key>::DetectElementId(&(iterMap.first)) ||
                    ElementIdDetectorHelper<T>::DetectElementId(&(iterMap.second)))
                {
                    return true;
                }
            }
            return false;
        }

        virtual void ReplaceElementIds(const std::unordered_map<ElementId, ElementId, ElementIdHash>& oldToNewMap) override
        {
            if (!HasElementIdProperty()) return;

            std::map<Key, T, KeyCompare> map;
            for (auto ite = m_map.begin(); ite != m_map.end(); ++ite)
            {
                Key k = NdbCopyHelper<Key>::Copy(ite->first);
                ReplaceElementIdHelper<Key>::ReplaceElementId(&k, oldToNewMap);
                ReplaceElementIdHelper<T>::ReplaceElementId(&(ite->second), oldToNewMap);
                map[k] = std::move(ite->second);
            }
            m_map.swap(map);
        }

        virtual void ReportElementIds(
            std::set<ElementId>* pModelIds,
            std::unordered_map<ElementId, std::vector<std::wstring>, ElementIdHash>* pPaths, 
            std::vector<std::wstring>* pCurrentPath
        ) const override
        {
            DBG_WARN_AND_RETURN_VOID_UNLESS(pModelIds, L"pModelIds不应该为空", L"GMEP", L"2016/10/14");
            if (!HasElementIdProperty()) return;

            for (auto ite = m_map.begin(); ite != m_map.end(); ++ite)
            {
                ReportElementIdHelper<Key>::ReportElementId(ite->first, pModelIds, pPaths, pCurrentPath);
                ReportElementIdHelper<T>::ReportElementId(ite->second, pModelIds, pPaths, pCurrentPath);
            }
        }

        virtual void CollectModuleGroupsOfNdbObject(std::set<Guid, GuidComparer>& moduleGroupsGuids) const
        {
            for (auto ite = m_map.begin(); ite != m_map.end(); ++ite)
            {
                NdbPropertySerialize<T>::CollectModuleGroups(ite->second, moduleGroupsGuids);
            }
        }

        virtual void CheckTopOwnerObject(std::vector<const UniqueWString*>& path,const NdbObject* correctowner,
            const NdbClassSchema* pNdbClassSchema, std::vector<std::wstring>& resultstr) const override
        {
            const std::wstring& subpropertyname = UniqueWString::Find(L"")->GetContent();
            std::vector<const UniqueWString*> localpath;
            localpath.insert(localpath.end(), path.begin(), path.end());
            localpath.push_back(UniqueWString::Find(L""));

            for (auto ite = m_map.begin(); ite != m_map.end(); ++ite)
            {
                if (!std::is_base_of<IDbObject, T>::value)
                {
                    auto result = Convert_Type_If<std::is_base_of<NdbObject, T>::value, property_is_owner_ptr_type<T>::value, NdbObject, T>::GetData(ite->second);
                    NdbObject* pObject = GetNonConstPoint<NdbObject*>::GetNonConstPointImp(result);

                    if (pObject != nullptr)
                    {
                        pObject->CheckTopOwnerObject(localpath, correctowner, nullptr, resultstr);

                    }
                }
                else
                {
                    auto result = Convert_Type_If<std::is_base_of<IDbObject, T>::value, property_is_owner_ptr_type<T>::value, IDbObject, T>::GetData(ite->second);
                    IDbObject* pPropertyIDbObject = GetNonConstPoint<IDbObject*>::GetNonConstPointImp(result);
                    if (pPropertyIDbObject != nullptr)
                    {
                        NdbObject* pPropertyNDbObject = dynamic_cast<NdbObject*>(pPropertyIDbObject);
                        if (pPropertyNDbObject == nullptr)
                            continue;

                        pPropertyNDbObject->CheckTopOwnerObject(localpath, correctowner, nullptr, resultstr);

                    }

                }

                if (!std::is_base_of<IDbObject, Key>::value)
                {
                    auto result = Convert_Type_If<std::is_base_of<NdbObject, Key>::value, property_is_owner_ptr_type<Key>::value, NdbObject, Key>::GetData(ite->first);
                    NdbObject* pObject = GetNonConstPoint<NdbObject*>::GetNonConstPointImp(result);

                    if (pObject != nullptr)
                    {
                        pObject->CheckTopOwnerObject(localpath, correctowner, nullptr, resultstr);

                    }

                }
                else
                {
                    auto result = Convert_Type_If<std::is_base_of<IDbObject, Key>::value, property_is_owner_ptr_type<Key>::value, IDbObject, Key>::GetData(ite->first);
                    IDbObject* pPropertyIDbObject = GetNonConstPoint<IDbObject*>::GetNonConstPointImp(result);
                    if (pPropertyIDbObject != nullptr)
                    {
                        NdbObject* pPropertyNDbObject = dynamic_cast<NdbObject*>(pPropertyIDbObject);
                        if (pPropertyNDbObject == nullptr)
                            continue;

                        pPropertyNDbObject->CheckTopOwnerObject(localpath, correctowner, nullptr, resultstr);

                    }

                }
            }

        }

    private:
        std::map<Key, T, KeyCompare> m_map;
    };
}
